/* Trabajo Practico 4 (Primera Entrega)
# Servidor Maestro
#
# - Integrantes:
#	- Martinez, Guillermo		DNI: 34510461
#	- Rodriguez, Lucas			DNI: 34265017
*/

#include "include/defines.h"
#include "include/sockets/sockets.h"
#include <vector>
#include "Mensajes.h"
#include "DatosJuego.h"
#include "servPartida.h"

using namespace std;       

ConfiguracionServidor ConfigServer =
{
	23456,//Puerto;
	3	 // CantJuegosPorPartida;
};

Mensaje msj;

vector<pid_t> OrganizarPartidos(int ronda);
DatosJugador CrearFantasma();

void *ThreadPrimeraRonda (void *Parm);
DatosJugador EsperaJugador(int sock_descriptor);

// Handler que finaliza servidor
void CerrarDescriptores(int);

// Funcion error
void error(const char*);

// Sockets
int sock_descriptor;
void InicializarSocket();

vector<DatosJugador> Jugadores;
const char _jugadores[]= "/_jugadores";
sem_t* mutex_jugadores;
sem_t cliente_conectado;
void CrearMutexJugadores();
void LiberarMutexJugadores();

bool AceptaConexiones = true;

int main()
{
	umask(0);

	// Manejar la finalizacion
	signal(SIGUSR1,CerrarDescriptores);
	signal(SIGINT ,CerrarDescriptores);
	signal(SIGTERM,CerrarDescriptores);

	InicializarSocket();

	CrearMutexJugadores();
	CrearMemoriaCompartida();

	pthread_t IdThreadPrimeraRonda;
	
	if (pthread_create (&IdThreadPrimeraRonda, NULL, ThreadPrimeraRonda, NULL) != 0)//vinculado * defecto
		error("Error al crear thread de la Ronda Inicial");


	////////////////////////////////////////////////////////////////////////////
	vector<pid_t> ServidoresPartidaRonda;
	int Ronda=1;
	bool TorneoTerminado = false;
	while(!TorneoTerminado)
	{
		cout << "------------------ Comienza Ronda" << Ronda << endl;
		
		int PartidosJugadosRonda = 0;
		bool RondaFinalizada = false;
		while(!RondaFinalizada)
		{
			const EstadoPartida Partida = LeerMemoriaCompartida();

			bool PartidoFinalizado=false;
			
			cout << getpid() << ">> " << Partida.Jugador1.Nombre << " " << Partida.PuntajeJug1
							 << " - " << Partida.Jugador2.Nombre << " " << Partida.PuntajeJug2 <<endl;
			
			P(mutex_jugadores);
			
			Jugadores[Partida.Jugador1.Id].Conectado = Partida.Jugador1.Conectado;
			Jugadores[Partida.Jugador2.Id].Conectado = Partida.Jugador2.Conectado;
			
			if(Partida.PuntajeJug1+Partida.PuntajeJug2 == ConfigServer.CantJuegosPorPartida)
			{
				//Termina una partida - Se actualiza el estado de los jugadores				
				PartidoFinalizado=true;

				if(!Partida.Jugador1.Fantasma && !Partida.Jugador2.Fantasma && Partida.Jugador1.Conectado)
					Jugadores[Partida.Jugador1.Id].PartidosJugados++;
				if(!Partida.Jugador1.Fantasma && !Partida.Jugador2.Fantasma && Partida.Jugador2.Conectado)
					Jugadores[Partida.Jugador2.Id].PartidosJugados++;
				
				if(Partida.PuntajeJug1 > Partida.PuntajeJug2)
				{	//Gano J1, verificamos que este conectado y no haya fantasmas
					if(!Partida.Jugador1.Fantasma && !Partida.Jugador2.Fantasma && Partida.Jugador1.Conectado)
					{
						Jugadores[Partida.Jugador1.Id].PartidosGanados++;
						cout << getpid() << ">> " << Partida.Jugador1.Nombre << " vs " << Partida.Jugador2.Nombre
										 << " | Ganador " << Partida.Jugador1.Nombre << endl;
					}
					
				}
				else
				{	//Gano J2, verificamos que este conectado y no sea fantasma
					if(!Partida.Jugador1.Fantasma && !Partida.Jugador2.Fantasma && Partida.Jugador2.Conectado)
					{
						Jugadores[Partida.Jugador2.Id].PartidosGanados++;
						cout << getpid() << ">> " << Partida.Jugador1.Nombre << " vs " << Partida.Jugador2.Nombre
										 << " | Ganador " << Partida.Jugador2.Nombre << endl;
					}
				}
					
				PartidosJugadosRonda++;
	
				if(PartidosJugadosRonda == Jugadores.size() / 2)
				{
					//Termina una Ronda
					RondaFinalizada	= true;
					
					if(Ronda == 1)
					{
						//No se aceptan mas jugadores
						AceptaConexiones=false;
						cout << "AceptaConexiones=false" << endl;
						
						if(Jugadores.size()%2) // Impar, agrego fantasma
							Jugadores.push_back( CrearFantasma() );

					}
				}
			}//Termina uno de los juegos ( mandados como parametro: el mejor de ....)
			
			V(mutex_jugadores);

			if(PartidoFinalizado)
				waitpid(Partida.IdServidorPartida, NULL, 0);

			if(RondaFinalizada && Ronda==1)
				pthread_join(IdThreadPrimeraRonda, NULL);	
		}//Termina ronda

		//Aqui no hace falta proteger Jugadores porque el thread de Primera Ronda ya finalizo 
		if(Ronda == Jugadores.size()-1)
		{
			//Termina el torneo
			TorneoTerminado = true;
		}
		
		Ronda++;

		if(!TorneoTerminado)
		{
			//Preparo los partidos para la proxima ronda segun algoritmo de LIGA
			ServidoresPartidaRonda=OrganizarPartidos(Ronda);
		}
	}//Fin torneo
	////////////////////////////////////////////////////////////////////////////
	
	DatosJugador * Campeon = &Jugadores[0];
	cout << "------------------ TABLA DE POSICIONES-------------------------" << endl;
	for(vector<DatosJugador> :: iterator ite=Jugadores.begin();ite!=Jugadores.end(); ite++)
	{
		cout << ite->Nombre << ": " << ite->PartidosGanados << " puntos (PJ " << ite->PartidosJugados << ")" << endl;
		
		if(ite->PartidosGanados > Campeon->PartidosGanados)
			Campeon = &(*ite);
	}
	
	cout << "--------------------------------------" << endl;
	cout << "Campeon del torneo " << Campeon->Nombre << endl;
	
	s_mov mov;
	mov.estado = CAMPEON;
	send(Campeon->Descriptor,&mov,sizeof(s_mov),0);
	sleep(2);
	
	CerrarDescriptores(0);
	exit(0);
}

void CerrarDescriptores(int signal)
{
	LiberarMutexJugadores();
	LiberarMemoriaCompartida();

	//CERRAR DESCRIPTORES
	for(vector<DatosJugador> :: iterator ite=Jugadores.begin();ite!=Jugadores.end(); ite++)
	{
		close(ite->Descriptor);
	}
	close(sock_descriptor);

	exit(signal);
}

void error(const char *e)
{
	fprintf(stdout,"%d>> (Torneo) %s\n",getpid(),e);
	
	//CERRAR DESCRIPTORES
	CerrarDescriptores(-1);
	fcloseall();
	exit(1);
}

DatosJugador EsperaJugador(int sock_descriptor)
{
	struct sockaddr_in socketdir_cliente; 
	int addrlen;
	int sock_descriptor_cli=0;

	// Esperar que algun cliente solicite servicio
	while(sock_descriptor_cli==0)
	{
		fd_set rfds;
		struct timeval tv;
		int retval;

		FD_ZERO(&rfds);
		FD_SET(sock_descriptor, &rfds);
		tv.tv_sec = 0;
		tv.tv_usec = 1000;

		retval = select(sock_descriptor+1, &rfds, NULL, NULL, &tv);

		if (retval == -1)
			error("Select()");
		else if (retval)
		{
			if(FD_ISSET(sock_descriptor, &rfds))
				sock_descriptor_cli = accept(sock_descriptor, NULL, 0);
		}
		
		else
		{
			P(mutex_jugadores);
			if(AceptaConexiones)
				sock_descriptor_cli=0;
			else
				sock_descriptor_cli=-2;
			V(mutex_jugadores);
		}
	}

	if(sock_descriptor_cli==-1)
		error("Error en accept");

	P(mutex_jugadores); //El V() se hace en ThreadPrimeraRonda

	DatosJugador Jugador;
	Jugador.Conectado=false;

	if(AceptaConexiones)
	{
		// Recibir msj del cliente
		Mensaje msj;
		if(recv(sock_descriptor_cli, &msj, sizeof(msj), 0) == -1)
			error("Error en recv");

		Jugador.Id=Jugadores.size();
		strcpy(Jugador.Nombre, msj.Nombre);
		Jugador.Conectado=true;
		Jugador.Fantasma=false;
		Jugador.Descriptor=sock_descriptor_cli;
		Jugador.PartidosGanados=0;
		Jugador.PartidosJugados=0;

		cout << getpid() << ">> Se conecto: " << Jugador.Nombre << endl;
	}
	
	else
	{
		cout << "No se aceptan mas jugadores" << endl;
		close(sock_descriptor_cli);
	}

	return Jugador;
}

void *ThreadPrimeraRonda (void *Parm)
{
	const int Ronda=1;//Hilo de ejecución solo para la primera ronda

	while(1)
	{
		DatosJugador Jugador1 = EsperaJugador(sock_descriptor);
		if(!AceptaConexiones)
			break;
		Jugadores.push_back(Jugador1);
		V(mutex_jugadores); //El P() se hace en EsperaJugador

		DatosJugador Jugador2 = EsperaJugador(sock_descriptor);
		if(!AceptaConexiones)
			break;
		Jugadores.push_back(Jugador2);
		V(mutex_jugadores); //El P() se hace en EsperaJugador

		CrearServidorPartida(Jugador1, Jugador2, Ronda);
	}

	V(mutex_jugadores); //El P() se hace en EsperaJugador
    close(sock_descriptor);
	pthread_exit(NULL);
}

void InicializarSocket()
{
	sv_config config;
	
	// Parámetros de servidor leídos del archivo de configuración
	configurar_servidor(&config);
	
	ConfigServer.Puerto = config.puerto;
	ConfigServer.CantJuegosPorPartida = config.cant_partidas;
	printf("Parámetros server: Puerto=%d | Cantidad_p=%d\n",ConfigServer.Puerto,ConfigServer.CantJuegosPorPartida);
	
	// Obtenemos un socket tipo internet
	if((sock_descriptor = socket(AF_INET, SOCK_STREAM, 0)) == -1)
		error("Error en creación socket");

	// Información del host
	struct sockaddr_in socketdir;	       		// Dir socket servidor
	socketdir.sin_family = AF_INET;
	socketdir.sin_addr.s_addr = INADDR_ANY;
	socketdir.sin_port = htons(ConfigServer.Puerto);

	// Eliminamos tiempo de espera luego de uso del socket
	int val=1;	
	if(setsockopt(sock_descriptor, SOL_SOCKET, SO_REUSEADDR, &val, sizeof(int)) == -1)
		error("Error en socket reuse");
		
	// Bind
	if(bind(sock_descriptor, (struct sockaddr *) &socketdir, sizeof(socketdir)) == -1)
		error("Error en bind");

	// Escuchar en el socket
	if(listen(sock_descriptor, 5) == -1)
		error("Error en listen");
}

void CrearMutexJugadores()
{
	if((mutex_jugadores = sem_open(_jugadores, O_CREAT | O_EXCL ,0666, 1)) == SEM_FAILED)
		error("No se puede abrir el semáforo\n");
}

void LiberarMutexJugadores()
{
	if(mutex_jugadores)
	{
		sem_close(mutex_jugadores);
		sem_unlink(_jugadores);
	}
}

DatosJugador CrearFantasma()
{
	 cout << "Se crea fantasma" << endl;
	 DatosJugador fantasma = {
								Jugadores.size(),	// Id;
								"FANTASMA",			// Nombre[TAMNOMBRE];
								true,				// Conectado;
								true,				// Fantasma;
								0,					// Descriptor;
								0,					// PartidosJugados;
								0					// PartidosGanados;
							};
	return fantasma;
}

vector<pid_t> OrganizarPartidos(int ronda)
{
	const int rondas = Jugadores.size() - 1;
	const int partidosPorRonda = Jugadores.size() / 2;

	cout << " --- Preparando Ronda " << ronda << endl;
	vector<DatosJugador> auxiliar;
	for(vector<DatosJugador>::reverse_iterator it=Jugadores.rbegin(); it!=Jugadores.rend(); ++it)
	{	//Ajuste primera ronda
		if( (it->Id) % 2 )
			auxiliar.insert(auxiliar.begin(), *it);
		else
			auxiliar.push_back(*it);
	}

	for(int i=0; i<ronda-1; i++)
	{
		DatosJugador aux = auxiliar.back();
		auxiliar.pop_back();
		auxiliar.insert(auxiliar.begin()+1, aux);
	}

	vector<pid_t> ServidoresPartida;
	for(int i=0; i<partidosPorRonda; i++)
	{
		const DatosJugador J1 = auxiliar[i];
		const DatosJugador J2 = auxiliar[Jugadores.size()-1-i];

		CrearServidorPartida(J1, J2, ronda);

		cout << "\t" << J1.Nombre << " vs " << J2.Nombre;
		if (J1.Fantasma || J2.Fantasma) 
			cout << " (no se juega)";
		cout << endl;
	}
	return ServidoresPartida;
}
